! ******************************************************
! Parallel Output
! ******************************************************

module Output
    use Input
    implicit none

contains

! ****** HDF5 Format Output *******
    subroutine dumpDatas()
        implicit none
        character(len=5) :: tid
        character(len=30) :: filename

        ! Setup file name.
        write(tid,"(i5)")DumpNumber
        filename = "../data/"//trim(adjustl(tid))//".h5"

        ! Initialize FORTRAN predefined datatypes.
        call h5open_f(error) 
     
        ! Setup file access property list with parallel I/O access.
        call h5pcreate_f(H5P_FILE_ACCESS_F, plist_id, error)
        call h5pset_fapl_mpio_f(plist_id, MPI_COMM_WORLD, MPI_INFO_NULL, error)

        ! Create the file collectively.
        call h5fcreate_f(filename, H5F_ACC_TRUNC_F, file_id, error, &
                         access_prp = plist_id)
        call h5pclose_f(plist_id, error)

        ! Save datas.
        if(neDump) call outputGrid(ne,'ne')
        if(niDump) call outputGrid(ni,'ni')
        if(uxDump) call outputGrid(ux,'ux')
        if(uyDump) call outputGrid(uy,'uy')
        if(uzDump) call outputGrid(uz,'uz')
        if(peDump) call outputGrid(pe,'pe')
        if(ExDump) call outputGrid(Ex,'Ex')
        if(EyDump) call outputGrid(Ey,'Ey')
        if(EzDump) call outputGrid(Ez,'Ez')
        if(BxDump) call outputGrid(Bx,'Bx')
        if(ByDump) call outputGrid(By,'By')
        if(BzDump) call outputGrid(Bz,'Bz')
        if(BwxDump) call outputGrid(Bwx,'Bwx')
        if(BwyDump) call outputGrid(Bwy,'Bwy')
        if(BwzDump) call outputGrid(Bwz,'Bwz')
        if(IonsDump) call outputParticle(IonListHead,'Ions')
        
        ! Close the file.
        call h5fclose_f(file_id, error)
        call h5close_f(error)

        ! Update Number of Output Datas
        DumpNumber = DumpNumber+1
    end subroutine dumpDatas

    subroutine outputGrid(Datas,dsetname)
        implicit none
        real(kind=8) :: Datas(-1-ncx:Nx+ncx+1,-1-ncy:Ny+ncy+1)
        real(kind=8) :: temp_Datas(-ncx:Nx+ncx+1,-ncy:Ny+ncy+1)
        character(len=*) :: dsetname
        real(kind=8), allocatable :: Temp(:,:)

        dimsf = (/NxBox+1,NyBox+1/)
        datasize = (/Nx,Ny/)
        if(ppi == ppx-1) datasize(1) = Nx+1
        if(ppj == ppy-1) datasize(2) = Ny+1

        allocate(Temp(datasize(1),datasize(2)))
        if(loc(Datas)==loc(Bx) .or. loc(Datas)==loc(By) .or. loc(Datas)==loc(Bz) &
        .or. loc(Datas)==loc(Bwx) .or. loc(Datas)==loc(Bwy) .or. loc(Datas)==loc(Bwz))then
            temp_Datas = mapBfield2Grid(Datas)
            Temp = temp_Datas(0:datasize(1)-1,0:datasize(2)-1)
        elseif(loc(Datas)==loc(Ex) .or. loc(Datas)==loc(Ey) .or. loc(Datas)==loc(Ez))then
            temp_Datas = mapEfield2Grid(Datas)
            Temp = temp_Datas(0:datasize(1)-1,0:datasize(2)-1)
        elseif(loc(Datas)==loc(pe))then
            Temp = p_unit*Datas(0:datasize(1)-1,0:datasize(2)-1)
        elseif(loc(Datas)==loc(ne) .or. loc(Datas)==loc(ni))then
            Temp = n_unit*Datas(0:datasize(1)-1,0:datasize(2)-1)
        else
            Temp = Datas(0:datasize(1)-1,0:datasize(2)-1)
        end if

        offset(1) = ppi*ceiling(real(NxBox,8)/ppx)
        offset(2) = ppj*ceiling(real(NyBox,8)/ppy)

        ! Create the data space for the dataset. 
        CALL h5screate_simple_f(ndim, dimsf, dataspace, error)
        ! Create the dataset with default properties.
        CALL h5dcreate_f(file_id, dsetname, H5T_NATIVE_DOUBLE, &
                         dataspace,dset_id, error)

        CALL h5screate_simple_f(ndim, datasize, memspace, error)
     
        ! Select hyperslab in the file.
        CALL h5sselect_hyperslab_f(dataspace, H5S_SELECT_SET_F, offset, &
                                    datasize, error)
     
        ! Create property list for collective dataset write.
        CALL h5pcreate_f(H5P_DATASET_XFER_F, plist_id, error) 
        CALL h5pset_dxpl_mpio_f(plist_id, H5FD_MPIO_COLLECTIVE_F, error)
     
        CALL h5dwrite_f(dset_id, H5T_NATIVE_DOUBLE, Temp, datasize, error, &
                        file_space_id = dataspace, mem_space_id = memspace, &
                        xfer_prp = plist_id)
     
        ! Close dataspaces.
        CALL h5sclose_f(dataspace, error)
        CALL h5sclose_f(memspace, error)
        ! Close the dataset and property list.
        CALL h5dclose_f(dset_id, error)
        CALL h5pclose_f(plist_id, error)

        deallocate(Temp)
    end subroutine outputGrid


    subroutine outputParticle(ListHead,dsetname)
        implicit none
        type(PseudoParticle), pointer :: ListHead
        character(len=*) :: dsetname
        type(PseudoParticle), pointer :: p
        real(kind=8), allocatable :: Temp(:,:)
        integer(kind=4) :: TotalIons  ! Total Particle Number
        integer(kind=4) :: FrontIons  ! Particle Number of Front MPI ID
        integer(kind=4) :: i

        call getIonsNumber(TotalIons,FrontIons)
       
        dimsf = (/8,TotalIons/)
        datasize = (/8,LocalIons/)
        
        offset(1) = 0
        offset(2) = FrontIons

        ! Initialize data buffer
        allocate(Temp(datasize(1),datasize(2))) 
        
        p => ListHead%next
        do i=1,LocalIons
            Temp(1,i) = real(p%Z,8)
            Temp(2,i) = real(p%A,8)
            Temp(3,i) = p%x
            Temp(4,i) = p%y
            Temp(5,i) = p%vx
            Temp(6,i) = p%vy
            Temp(7,i) = p%vz
            Temp(8,i) = p%weight
            p => p%next
        end do

        ! Create the data space for the dataset. 
        CALL h5screate_simple_f(ndim, dimsf, dataspace, error)
        ! Create the dataset with default properties.
        CALL h5dcreate_f(file_id, dsetname, H5T_NATIVE_DOUBLE, &
                         dataspace,dset_id, error)

        CALL h5screate_simple_f(ndim, datasize, memspace, error)
     
        ! Select hyperslab in the file.
        CALL h5sselect_hyperslab_f(dataspace, H5S_SELECT_SET_F, offset, &
                                    datasize, error)
     
        ! Create property list for collective dataset write.
        CALL h5pcreate_f(H5P_DATASET_XFER_F, plist_id, error) 
        CALL h5pset_dxpl_mpio_f(plist_id, H5FD_MPIO_COLLECTIVE_F, error)
     
        CALL h5dwrite_f(dset_id, H5T_NATIVE_DOUBLE, Temp, datasize, error, &
                        file_space_id = dataspace, mem_space_id = memspace, &
                        xfer_prp = plist_id)
     
        ! Close dataspaces.
        CALL h5sclose_f(dataspace, error)
        CALL h5sclose_f(memspace, error)
        ! Close the dataset and property list.
        CALL h5dclose_f(dset_id, error)
        CALL h5pclose_f(plist_id, error)

        deallocate(Temp)

    end subroutine outputParticle

    subroutine getIonsNumber(TotalIons,FrontIons)
        implicit none
        integer(kind=4) :: TotalIons  ! Total Particle Number
        integer(kind=4) :: FrontIons  ! Particle Number of Front MPI ID
        integer(kind=4) :: obj,TempIons
    
        if(myid==0)then
            obj = myid+1
            call MPI_SEND(LocalIons,1,mpi_integer,obj,1,mpi_comm_world,ierr)
            call MPI_RECV(TotalIons,1,mpi_integer,obj,1,mpi_comm_world, &
                          status,ierr)
            FrontIons = 0
        else if(myid==TotalProcessors-1)then
            obj = myid-1
            call MPI_RECV(FrontIons,1,mpi_integer,obj,1,mpi_comm_world, &
                          status,ierr)
            TotalIons = FrontIons+LocalIons
            call MPI_SEND(TotalIons,1,mpi_integer,obj,1,mpi_comm_world,ierr)
        else
            obj = myid-1
            call MPI_RECV(FrontIons,1,mpi_integer,obj,1,mpi_comm_world, &
                          status,ierr)
            TempIons = FrontIons+LocalIons

            obj = myid+1
            call MPI_SEND(TempIons,1,mpi_integer,obj,1,mpi_comm_world,ierr)
            call MPI_RECV(TotalIons,1,mpi_integer,obj,1,mpi_comm_world, &
                          status,ierr)
            obj = myid-1
            call MPI_SEND(TotalIons,1,mpi_integer,obj,1,mpi_comm_world,ierr)
        end if
    
    end subroutine getIonsNumber
end module Output